<?php

namespace app\common\service;

use app\common\model\Company;
use app\company\model\CompanyUser;
use app\company\model\MessageNotification;
use app\company\model\MessageNotificationRead;
use app\company\model\MessageNotificationType;
use think\facade\Db;

class MessageNotificationService
{
    private MessageNotification $model;
    private Company $company;
    private MessageNotificationRead $reads;
    private CompanyUser $companyUser;

    public function __construct()
    {
        $this->model = new MessageNotification();
        $this->company = new Company();
        $this->companyUser = new CompanyUser();
        $this->reads = new MessageNotificationRead();
    }

    /**
     * 创建通知
     *
     * @param string $type
     * @param string $title
     * @param string $content
     * @param int|null $companyId
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function createNotification(string $type, string $title, string $content, ?int $companyId = null): bool
    {
        if (!is_null($companyId) && $this->validateCompany($companyId)) {
            $companyIds = [$companyId];
        } else {
            $companyIds = $this->companyIds();
        }


        try {
            Db::startTrans();
            $type = $this->firstOrCreate(['name' => $type]);
            $data = $this->prepareNotificationData($title, $content, $type->id, $companyIds);

            foreach ($data as $value) {
                $message = $this->model->create($value);
                $userIds = $this->getUserIdsByCompanyId($value['company_id']);

                $reads = $this->prepareReadRecords($message->id, $userIds);
                $this->reads->saveAll($reads);
            }
            Db::commit();

            return true;
       } catch (\Exception $e) {
            Db::rollback();

            return false;
        }
    }

    /**
     * 准备记录数据
     *
     * @param int $messageId
     * @param array $userIds
     * @return array
     */
    private function prepareReadRecords(int $messageId, array $userIds): array
    {
        $reads = [];
        foreach ($userIds as $userId) {
            $reads[] = [
                'user_id' => $userId,
                'message_notification_id' => $messageId,
            ];
        }
        return $reads;
    }

    /**
     * 准备通知数据
     *
     * @param string $title
     * @param string $content
     * @param string $type
     * @param array $companies
     * @return array
     */
    private function prepareNotificationData(string $title, string $content, string $type, array $companies): array
    {
       $message = [];
       foreach ($companies as $company) {
           $message[] = [
               'title' => $title,
               'type' => $type,
               'content' => $content,
               'company_id' => $company,
           ];
       }

       return $message;
    }

    /**
     * 根据公司 ID 获取用户 ID 列表
     *
     * @param string $companyIds
     * @return array
     */
    private function getUserIdsByCompanyId(string $companyId): array
    {
        return $this->companyUser->where('company_id', $companyId)->column('id');
    }

    /**
     * 验证公司是否存在
     *
     * @param int $companyId
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function validateCompany(int $companyId): bool
    {
        return (bool)$this->company->find($companyId);
    }

    /**
     * 获取所有公司id
     * @return array
     */
    public function companyIds(): array
    {
        return $this->company->column('id');
    }

    /**
     * 实现 firstOrCreate 方法
     *
     * @param array $attributes
     * @param array $values
     * @return MessageNotificationType
     */
    public static function firstOrCreate(array $attributes, array $values = [])
    {
        // 尝试根据条件查找记录
        $typeModel = new MessageNotificationType();
        $type = $typeModel->where($attributes)->findOrEmpty();

        if ($type->isEmpty()) {
            $type->save(array_merge($attributes, $values));
        }

        return $type;
    }
}
